package malha;

import interfacesFerrovia.DefsGlobais.TipoAtributo;
import interfacesFerrovia.DefsGlobais.TipoElemento;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.Arrays;

import controle.CControle;



/**
 * Classe que controla a Malha e seus elementos
 */
public class CMalha {

	private HashMap<String, CChave> chaves = new HashMap<String, CChave>();
	private HashMap<String, CTrecho> trechos = new HashMap<String, CTrecho>();
	private HashMap<String, CComposicao> composicoes = new HashMap<String, CComposicao>();

	
	/**
	 * Atualiza a posicaoo das composicoes na malha de acordo com sua velocidade e aceleracaoo (ou frenagem).
	 * Tambem decerementa o temporizador da chave e da composicao
	 * 
	 * @pre A simulacao na malha esta ocorrendo -- OK
	 * 
	 * @inv A quantidade de elementos na malha nao eh alterado -- OK
	 * 
	 * @post Todas as chaves tem seu 'tempoConclusao' positivo decrementados de uma unidade -- OK
	 * @post As chaves cujo 'tempoConclusao' chegarem a zero tem seu a atributo 'emMudanca' atribuido o valor falso -- OK
	 * 
	 */
	public void atualizarMalha() {
		/* Pre */
		assert(CControle.getControle().getInstanteAtual() >= 0);
				
		/* Inv */
		int total = composicoes.size() + trechos.size() + chaves.size();
		
		
		/* Codigo */
		
		/* Itera nas composicoes */
		for (String idComp : composicoes.keySet()) {
			CComposicao comp = composicoes.get(idComp);			
			
			comp.atualizarPosicao();			
			comp.atualizarVelocidade();
			
			Integer tempConclusaoOld = comp.getTempoConclusao();
			Integer tempConclusaoNew = 0;
			
			if (tempConclusaoOld != 0) {
				comp.setTempoConclusao(tempConclusaoOld - 1);
				tempConclusaoNew = comp.getTempoConclusao();
				assert(tempConclusaoNew == tempConclusaoOld - 1);
			}
			
			if (tempConclusaoNew == 0) {
				comp.setAcelAtual(0);
				comp.setFrenAtual(0);
				assert(comp.getAcelAtual() == 0);
				assert(comp.getFrenAtual() == 0);
			}
		}
		
			
		for (String idChave : chaves.keySet()) {
			CChave chave = chaves.get(idChave);
			
			Integer tempConclusaoOld = chave.getTempoConclusao();
			Integer tempConclusaoNew = 0;
			
			if (tempConclusaoOld != 0) {
				chave.setTempoConclusao(tempConclusaoOld - 1);
				tempConclusaoNew = chave.getTempoConclusao();
				assert(tempConclusaoNew == tempConclusaoOld - 1);
			}
			
			if (tempConclusaoNew == 0) {
				chave.setEmMudanca(false);
				assert(chave.isEmMudanca() == false);
			}
		}
		
		/* Post */
		// Feito durante o codigo
		
		/* Inv */
		assert(total == composicoes.size() + trechos.size() + chaves.size());
		
		
		//debug
		/*
		for (String it : composicoes.keySet()) {
			Object o = composicoes.get(it);
			System.err.println(o.toString());
		}
		for (String it : chaves.keySet()) {
			Object o = chaves.get(it);
			System.err.println(o.toString());
		}*/
	}
	
	
	/**
	 * Define os valores dos atributos do elemento como sendo aqueles passados pelo Mapa atributos
	 * 
	 * @param idElemento Identificacao unica do elemento a ter seus atributos atualizados
	 * @param atributos Mapa associando o atributo a seu valor
	 * 
	 * @pre Elemento deve existir na malha -- OK
	 * @pre Os atributos passados pelo Mapa atributos deve ser coerentes com os atributos de elemento -- OK
	 * 
	 * @inv A quantidade de elementos na malha nao muda -- OK
	 * @inv Os elementos da malha cujo id eh diferente de idElemento nao sao alterados -- OK
	 * 
	 * @post Os atributos de elemento sao settados como os passados pelo Mapa atributos -- OK
	 */
	public void atualizarElemento(String idElemento, Map<TipoAtributo, Object> atributos) {
		/* Pre */
		CChave chave = chaves.get(idElemento);
		CTrecho trecho = trechos.get(idElemento);
		CComposicao composicao = composicoes.get(idElemento);
				
		assert((chave != null) && (trecho == null) && (composicao == null) ||
				(chave == null) && (trecho != null) && (composicao == null) ||
				(chave == null) && (trecho == null) && (composicao != null));
		
		
		/* Inv */
		int total = composicoes.size() + trechos.size() + chaves.size();
		
		HashMap<String, CChave> oldChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> oldTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> oldComposicoes = new HashMap<String, CComposicao>(composicoes);
		oldChaves.remove(idElemento);
		oldComposicoes.remove(idElemento);
		oldTrechos.remove(idElemento);
		
		
		/* Codigo */
		
		String tempArray[];
		List<String> list;
		
		if (chave != null) {
			for(TipoAtributo attrChave : atributos.keySet()) {
				Vector<String> vetorString = null;
				
				switch (attrChave) {
				case DISTANCIA: chave.setDistancia((Float)atributos.get(attrChave));
					break;
				
				case TRECHOS_A:
					tempArray = (String[]) atributos.get(attrChave);
					list = Arrays.asList(tempArray);
					vetorString = new Vector<String>(list);
					chave.setTrechosA(vetorString);
					break;
					
				case TRECHOS_B:
					tempArray = (String[]) atributos.get(attrChave);
					list = Arrays.asList(tempArray);
					vetorString = new Vector<String>(list);
					chave.setTrechosB(vetorString);
			
					break;
					
				case ID_TR_CH_A:
					String idTrechoA = (String)atributos.get(attrChave);			
					chave.setElementoAtualA(trechos.get(idTrechoA), true);
					break;
					
				case ID_TR_CH_B:
					String idTrechoB = (String)atributos.get(attrChave);
					chave.setElementoAtualB(trechos.get(idTrechoB), true);
					break;
					
				case TEMPO_MUDANCA: chave.setTempoMudanca((Integer)atributos.get(attrChave));
					break;
					
				case EM_MUDANCA: chave.setEmMudanca((Boolean)atributos.get(attrChave));
					break;
					
				case TEMPO_CONCLUSAO: chave.setTempoConclusao((Integer)atributos.get(attrChave));
					break;
					
				case ID: assert(chave.getId() == (String)atributos.get(attrChave));
					break;
				
				case TIPO: assert(TipoElemento.CHAVE == (TipoElemento)atributos.get(attrChave));
					break;
					
				default:
					assert(false);					
				}
			}
		} else if (trecho != null) {
			for(TipoAtributo attrTrecho : atributos.keySet()) {		
				switch (attrTrecho) {
				case DISTANCIA: trecho.setDistancia((Float)atributos.get(attrTrecho));
					break;
					
				case ID_TR_CH_A:
					String idChaveA = (String)atributos.get(attrTrecho);
					trecho.setElementoAtualA(chaves.get(idChaveA), true);
					
					break;
					
				case ID_TR_CH_B:
					String idChaveB = (String)atributos.get(attrTrecho);
					trecho.setElementoAtualB(chaves.get(idChaveB), true);
					
					break;
					
				case N_COMPOSICOES: trecho.setnComposicoes((Integer)atributos.get(attrTrecho));
					break;
					
				case ID: assert(trecho.getId() == (String)atributos.get(attrTrecho));
					break;
					
				case TIPO: assert(TipoElemento.TRECHO == (TipoElemento)atributos.get(attrTrecho));
					break;
					
				default:
					assert(false);
				}
			}
		} else if (composicao != null) {
			for(TipoAtributo attrComp : atributos.keySet()) {
				switch (attrComp) {
				case COMPRIMENTO: composicao.setComprimento((Integer)atributos.get(attrComp));
					break;
					
				case VELOCIDADE_MAX: composicao.setVelMax((Float)atributos.get(attrComp));
					break;
					
				case ACELERACAO_MAX: composicao.setAcelMax((Float)atributos.get(attrComp));
					break;
					
				case FRENAGEM_MAX: composicao.setFrenMax((Float)atributos.get(attrComp));
					break;
					
				case ITINERARIO:
					Object att = atributos.get(attrComp);
					String vetorString[] = (String[]) att;
					Vector<CElementoTrilho> vetorElem = new Vector<CElementoTrilho>();
					
					for (String it : vetorString) {
						CChave chaveTmp = chaves.get(it);
						CTrecho trechoTmp = trechos.get(it);
						
						if (chaveTmp != null) {
							vetorElem.add(chaveTmp);
						} else if (trechoTmp != null) {
							vetorElem.add(trechoTmp);
						} else {
							assert(false);
						}
					}
					
					composicao.setItinerario(vetorElem);
					break;
					
				case PARTIDA: composicao.setPartida((Integer)atributos.get(attrComp));
					break;
					
				case ID_TR_CH_ATUAL:
					String idTrChAtual = (String)atributos.get(attrComp);
					CChave chaveTmp = chaves.get(idTrChAtual);
					CTrecho trechoTmp = trechos.get(idTrChAtual);
					
					if (chaveTmp != null) {
						composicao.setTrechoChaveAtual(chaveTmp);
					} else if (trechoTmp != null) {
						composicao.setTrechoChaveAtual(trechoTmp);
					} else {
						assert(false);
					}
					
					break;
					
				case POSICAO_ATUAL: composicao.setPosicaoAtual((Float)atributos.get(attrComp));
					break;
					
				case VEL_ATUAL: composicao.setVelAtual((Float)atributos.get(attrComp));
					break;
					
				case SENTIDO_ATUAL: composicao.setMovendoAParaB((Boolean)atributos.get(attrComp));
					break;
					
				case ACEL_ATUAL: composicao.setAcelAtual((Float)atributos.get(attrComp));
					break;
					
				case FREN_ATUAL: composicao.setFrenAtual((Float)atributos.get(attrComp));
					break;
					
				case EM_MOVIMENTO: composicao.setEmMovimento((Boolean)atributos.get(attrComp));
					break;
					
				case ID: assert(composicao.getId() == (String)atributos.get(attrComp));
					break;
					
				case TIPO: assert(TipoElemento.COMPOSICAO == (TipoElemento)atributos.get(attrComp));
					break;
					
				default:
					assert(false);
				}
			}
		} else {
			assert(false);
		}
				
				
		/* Post */
		// Feito durante o codigo		
		
		/* Inv */
		assert(total == composicoes.size() + trechos.size() + chaves.size());
		
		HashMap<String, CChave> newChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> newTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> newComposicoes = new HashMap<String, CComposicao>(composicoes);
		newChaves.remove(idElemento);
		newComposicoes.remove(idElemento);
		newTrechos.remove(idElemento);
		
		assert(oldChaves.equals(newChaves));
		assert(oldTrechos.equals(newTrechos));
		assert(oldComposicoes.equals(newComposicoes));
	}
	
	
	/**
	 * Aumenta a velocidade de uma composicao
	 * Implementacao: OK
	 * 
	 * @param idComposicao Composicao a ter sua velocidade aumentada
	 * @param velocidade Nova velocidade da composicao
	 * @param tempoConclusao Tempo que a composicao levara para atingir a velocidade
	 * 
	 * @pre idComposicao deve existir na malha -- OK
	 * @pre idComposicao deve ser o identificador unico de uma composicao -- OK
	 * @pre A nova velocidade deve ser maior que a velocidade atual da composicao -- OK
	 * @pre A nova velocidade deve ser menor ou igual a velocidade maxima da composicao -- OK
	 * @pre tempoConclusao deve ser positivo -- OK
	 * 
	 * @inv Os elementos da malha cujo id eh diferente de idComposicao nao sao alterados -- OK
	 * @inv A quantidade de elementos da malha nao muda -- OK
	 * 
	 * @post A composicao tera aceleracao atual positiva -- OK
	 * @post A composicao tera frenagem atual igual a zero -- OK
	 */
	public void acelerarComposicao(String idComposicao, float velocidade, int tempoConclusao) {
		/* Pre */
		CComposicao composicao = composicoes.get(idComposicao);
		
		assert(composicao != null);
		
		assert(composicao.getVelMax() >= velocidade);
		assert(velocidade > composicao.getVelAtual());
		assert(velocidade > 0);
			
		assert(tempoConclusao > 0);
		
		/* Inv */
		int total = composicoes.size() + trechos.size() + chaves.size();

		HashMap<String, CChave> oldChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> oldTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> oldComposicoes = new HashMap<String, CComposicao>(composicoes);
		oldComposicoes.remove(idComposicao);
			
		/* Codigo */
		composicao.acelerarComposicao(velocidade, tempoConclusao);
			
		/* Post */
		assert(composicao.getAcelAtual() > 0);
		assert(composicao.getFrenAtual() == 0);
			
		/* Inv */
		assert(composicoes.size() + trechos.size() + chaves.size() == total);
		
		HashMap<String, CChave> newChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> newTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> newComposicoes = new HashMap<String, CComposicao>(composicoes);
		newComposicoes.remove(idComposicao);
		
		assert(oldChaves.equals(newChaves));
		assert(oldTrechos.equals(newTrechos));
		assert(oldComposicoes.equals(newComposicoes));
	}
	
	
	/**
	 * Aumenta a velocidade de uma composicao
	 * Implementacao: OK
	 * 
	 * @param idComposicao Composicao a ter sua velocidade aumentada
	 * @param velocidade Nova velocidade da composicao
	 * @param tempoConclusao Tempo que a composicao levara para atingir a velocidade
	 * @param aceleracao Aceleracao da composicao
	 * 
	 * @pre idComposicao deve existir na malha -- OK
	 * @pre idComposicao deve ser o identificador unico de uma composicao -- OK
	 * @pre A nova velocidade deve ser maior que a velocidade atual da composicao -- OK
	 * @pre A nova velocidade deve ser menor ou igual a velocidade maxima da composicao -- OK
	 * @pre A nova aceleracao deve ser maior que a aceleracao atual da composicao -- OK
	 * @pre A nova aceleracao deve ser menor ou igual a aceleracao maxima da composicao -- OK
	 * @pre tempoConclusao deve ser positivo -- OK
	 * 
	 * @inv Os elementos da malha cujo id eh diferente de idComposicao nao sao alterados -- OK
	 * @inv A quantidade de elementos da malha nao muda -- OK
	 * 
	 * @post A composicao tera aceleracao atual positiva -- OK
	 * @post A composicao tera frenagem atual igual a zero -- OK
	 */
	public void acelerarComposicao(String idComposicao, float velocidade, int tempoConclusao, float aceleracao) {
		/* Pre */
		CComposicao composicao = composicoes.get(idComposicao);
		
		assert(composicao != null);
		
		assert(composicao.getVelMax() >= velocidade);
		assert(velocidade > composicao.getVelAtual());
		assert(velocidade > 0);
		assert(aceleracao <= composicao.getAcelMax());
		
		assert(tempoConclusao > 0);
		
		/* Inv */
		int total = composicoes.size() + trechos.size() + chaves.size();

		HashMap<String, CChave> oldChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> oldTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> oldComposicoes = new HashMap<String, CComposicao>(composicoes);
		oldComposicoes.remove(idComposicao);
			
		/* Codigo */
		composicao.acelerarComposicao(velocidade, tempoConclusao, aceleracao);
		
		/* Post */
		assert(composicao.getAcelAtual() > 0);
		assert(composicao.getFrenAtual() == 0);
			
		/* Inv */
		assert(composicoes.size() + trechos.size() + chaves.size() == total);
		
		HashMap<String, CChave> newChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> newTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> newComposicoes = new HashMap<String, CComposicao>(composicoes);
		newComposicoes.remove(idComposicao);
		
		assert(oldChaves.equals(newChaves));
		assert(oldTrechos.equals(newTrechos));
		assert(oldComposicoes.equals(newComposicoes));
	}
		
	/**
	 * Reduz a velocidade de uma composicao
	 * Implementacao: OK
	 * 
	 * @param idComposicao Composicao a ter sua velocidade reduzida
	 * @param velocidade Nova velocidade da composicao
	 * @param tempoConclusao Tempo que a composicaoo levara para atingir a velocidade
	 * 
	 * @pre idComposicao deve existir na malha -- OK
	 * @pre idComposicao deve ser o identificador unico de uma composicao -- OK
	 * @pre A nova velocidade deve ser menor que a velocidade atual da composicao -- OK
	 * @pre tempoConclusao deve ser positivo -- OK
	 * 
	 * @inv Os elementos da malha cujo id eh diferente de idComposicao nao sao alterados -- OK
	 * @inv A quantidade de elementos da malha nao muda -- OK
	 * 
	 * @post A composicao tera frenagem atual positiva -- OK
	 * @post A composicaoo tera aceleracao atual igual a zero -- OK
	 */
	public void frearComposicao(String idComposicao, float velocidade, int tempoConclusao) {
		/* Pre */
		CComposicao composicao = composicoes.get(idComposicao);
		
		
		assert(velocidade < composicao.getVelAtual());
		assert(velocidade >= 0);
		assert(velocidade <= composicao.getVelMax());
				
		assert(tempoConclusao > 0);
		
		/* Inv */
		int total = composicoes.size() + trechos.size() + chaves.size();
		
		HashMap<String, CChave> oldChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> oldTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> oldComposicoes = new HashMap<String, CComposicao>(composicoes);
		oldComposicoes.remove(idComposicao);
			
		/* Codigo */
		composicao.frearComposicao(velocidade, tempoConclusao);
		
		/* Post */
		assert(composicao.getAcelAtual() == 0);
		assert(composicao.getFrenAtual() > 0);
		
		/* Inv */
		assert(composicoes.size() + trechos.size() + chaves.size() == total);
		

		HashMap<String, CChave> newChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> newTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> newComposicoes = new HashMap<String, CComposicao>(composicoes);
		newComposicoes.remove(idComposicao);
				
		assert(oldChaves.equals(newChaves));
		assert(oldTrechos.equals(newTrechos));
		assert(oldComposicoes.equals(newComposicoes));
	}
	
	
	/**
	 * Reduz a velocidade de uma composicao
	 * Implementacao: OK
	 * 
	 * @param idComposicao Composicao a ter sua velocidade reduzida
	 * @param velocidade Nova velocidade da composicao
	 * @param tempoConclusao Tempo que a composicaoo levara para atingir a velocidade
	 * 
	 * @pre idComposicao deve existir na malha -- OK
	 * @pre idComposicao deve ser o identificador unico de uma composicao -- OK
	 * @pre A nova velocidade deve ser menor que a velocidade atual da composicao -- OK
 	 * @pre A nova frenagem deve ser maior que a frenagem atual da composicao -- OK
	 * @pre A nova frenagem deve ser menor ou igual a frenagem maxima da composicao -- OK
	 * @pre tempoConclusao deve ser positivo -- OK
	 * 
	 * @inv Os elementos da malha cujo id eh diferente de idComposicao nao sao alterados -- OK
	 * @inv A quantidade de elementos da malha nao muda -- OK
	 * 
	 * @post A composicao tera frenagem atual positiva -- OK
	 * @post A composicaoo tera aceleracao atual igual a zero -- OK
	 */
	public void frearComposicao(String idComposicao, float velocidade, int tempoConclusao, float frenagem) {
		/* Pre */
		CComposicao composicao = composicoes.get(idComposicao);
		
		assert(composicao != null);
				
		assert(velocidade < composicao.getVelAtual());
		assert(velocidade >= 0);
		assert(velocidade <= composicao.getVelMax());
		assert(frenagem <= composicao.getFrenMax());
		
		assert(tempoConclusao > 0);
		
		/* Inv */
		int total = composicoes.size() + trechos.size() + chaves.size();
		
		HashMap<String, CChave> oldChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> oldTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> oldComposicoes = new HashMap<String, CComposicao>(composicoes);
		oldComposicoes.remove(idComposicao);
	
		/* Codigo */
		composicao.frearComposicao(velocidade, tempoConclusao, frenagem);
			
		
		/* Post */
		assert(composicao.getAcelAtual() == 0);
		assert(composicao.getFrenAtual() > 0);
		
		/* Inv */
		assert(composicoes.size() + trechos.size() + chaves.size() == total);

		HashMap<String, CChave> newChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> newTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> newComposicoes = new HashMap<String, CComposicao>(composicoes);
		newComposicoes.remove(idComposicao);
				
		assert(oldChaves.equals(newChaves));
		assert(oldTrechos.equals(newTrechos));
		assert(oldComposicoes.equals(newComposicoes));
	}
	
	
	/**
	 * Adiciona uma composicao na malha.
	 * Implementacao: OK
	 * 
	 * @param idComposicao Identificacao unica(id) da composicao
	 * 
	 * @pre idComposicao dever ser a identificacao unica de uma composicao -- OK
	 * @pre idComposicao nao pode ser igual a nenhuma outra id ja adicionada na malha -- OK  
	 * 
	 * @inv Os elementos da malha cujo id eh diferente de idComposicao nao sao alterados -- OK
	 * 
	 * @post Uma composicao CComposicao eh adidionada a malha -- OK
	 * @post O id desta CComposicao deve ser igual a idComposicao -- OK
	 * @post Nenhum atributo de CComposicao eh setado (exceto o id) -- OK
	 * @post A quantidade de elementos na malha aumenta em uma unidade -- OK
	 */
	public void adicionarComposicao(String idComposicao) {
		/* Pre */
		assert(composicoes.containsKey(idComposicao) == false);
		
		int totalComp = composicoes.size();
		int total = composicoes.size() + trechos.size() + chaves.size();
			
		/* Inv */
		HashMap<String, CChave> oldChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> oldTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> oldComposicoes = new HashMap<String, CComposicao>(composicoes);
		oldComposicoes.remove(idComposicao);
		
		/* Codigo */
		CComposicao novaComp = null;
		novaComp = new CComposicao(idComposicao);
		assert(novaComp != null);
		composicoes.put(idComposicao, novaComp);
			
		/* Post */
		assert(totalComp + 1 == composicoes.size());
		assert(total + 1 == composicoes.size() + trechos.size() + chaves.size());
		
		assert(composicoes.containsKey(idComposicao) == true);
		
		/* Inv */
		HashMap<String, CChave> newChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> newTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> newComposicoes = new HashMap<String, CComposicao>(composicoes);
		newComposicoes.remove(idComposicao);
				
		assert(oldChaves.equals(newChaves));
		assert(oldTrechos.equals(newTrechos));
		assert(oldComposicoes.equals(newComposicoes));
	}
	
	
	/**
	 * Remove uma composicao da Malha
	 * Implementacao: OK
	 * 
	 * @param idComposicao Identificacao unica da composicao
	 * 
	 * @pre idComposicao deve existir na malha -- OK
	 * @pre icComposicao deve ser a identificacao unica de uma composicao -- OK
	 * 
	 * @inv Os elementos da malha cujo id eh diferente de idComposicao nao sao alterados -- OK
	 * 
	 * @post A composicao cujo id eh idComposicao eh removido da malha -- OK
	 * @post A quantidade de elementos na malha diminui em uma unidade -- OK
	 */
	public void removerComposicao(String idComposicao) {
		/* Pre */
		int total = composicoes.size();
		
		assert(composicoes.containsKey(idComposicao) == true);
		
		/* Inv */
		HashMap<String, CChave> oldChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> oldTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> oldComposicoes = new HashMap<String, CComposicao>(composicoes);
		oldComposicoes.remove(idComposicao);
		
		
		/* Codigo */
		composicoes.remove(idComposicao);
		
		
		/* Post */
		assert(composicoes.containsKey(idComposicao) == false);
		
		assert(total - 1 == composicoes.size());
		
		/* Inv */
		HashMap<String, CChave> newChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> newTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> newComposicoes = new HashMap<String, CComposicao>(composicoes);
		newComposicoes.remove(idComposicao);
				
		assert(oldChaves.equals(newChaves));
		assert(oldTrechos.equals(newTrechos));
		assert(oldComposicoes.equals(newComposicoes));
	}
		
	/**
	 * Muda a configuracao de uma chave
	 * Implementacao: OK
	 * 
	 * @param idChave Identificacao unica da chave
	 * @param idTrechoA Trecho para o qual a extremidade A sera posicionado
	 * @param idTrechoB Trecho para o qual a extremidade B sera posicionado
	 * 
	 * @pre idChave deve existir na malha -- OK
	 * @pre idChave deve ser o id de uma chave -- OK
	 * @pre idTrechoA deve existir na malha -- OK
	 * @pre idTrechoA deve ser o id de um trecho -- OK
	 * @pre idTrechoA dever ser diferente de ElementoAtualA (trecho atual em A) de idChaveA [*] -- OK
	 * @pre idTrechoA deve ser um dos trechos disponiveis para conexao na extremidade A -- OK
	 * @pre idTrechoB deve existir na malha -- OK
	 * @pre idTrechoB deve ser o id de um trecho -- OK
	 * @pre idTrechoB dever ser diferente de ElementoAtualB (trecho atual em B) de idChaveB [*] -- OK
	 * @pre idTrechoB deve ser um dos trechos disponiveis para conexao na extremidade B -- OK
	 * 
	 * @inv Os elementos da malha cujo id eh diferente de idChave nao sao alterados -- OK
	 * @inv A quantidade de elementos da malha nao muda -- OK
	 * 
	 * @post O trecho conectado a extremidade A sera o trecho cujo id eh idtrechoA -- OK
	 * @post O trecho conectado a extremidade B sera o trecho cujo id eh idtrechoB -- OK
	 */
	public void mudarChave(String idChave, String idTrechoA, String idTrechoB) {
		/* Pre */
		CChave chave = chaves.get(idChave);
		assert(chave != null);
		
		CTrecho trechoA = trechos.get(idTrechoA);
		CTrecho trechoB = trechos.get(idTrechoB);
		assert(trechoA != null);
		assert(trechoB != null);
				
		assert((chave.getElementoAtualA().getId() != idTrechoA) ||
				(chave.getElementoAtualB().getId() != idTrechoB));
		
		assert(chave.getTrechosA().contains(idTrechoA) == true);
		assert(chave.getTrechosB().contains(idTrechoB) == true);
		
		/* Inv */
		int total = composicoes.size() + trechos.size() + chaves.size();
		
		HashMap<String, CChave> oldChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> oldTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> oldComposicoes = new HashMap<String, CComposicao>(composicoes);
		oldChaves.remove(idChave);
		
		
		/* Codigo */
		chave.mudarChave(trechoA, trechoB);
		
		
		/* Post */
		assert(chave.getElementoAtualA().getId() == idTrechoA);
		assert(chave.getElementoAtualB().getId() == idTrechoB);
		
		/* Inv */
		assert(total == composicoes.size() + trechos.size() + chaves.size());
		
		HashMap<String, CChave> newChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> newTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> newComposicoes = new HashMap<String, CComposicao>(composicoes);
		newChaves.remove(idChave);
				
		assert(oldChaves.equals(newChaves));
		assert(oldTrechos.equals(newTrechos));
		assert(oldComposicoes.equals(newComposicoes));
	}
	
	
	/**
	 * Adiciona uma chave na malha
	 * Implementacao: OK
	 * 
	 * @param idChave Identificacao unica(id) de uma chave
	 * 
	 * @pre idChave deve ser a identificacao unica de uma chave -- OK
	 * @pre idChave nao pode ser igual a nenhuma outra id ja adicionada na malha -- OK
	 * 
	 * @inv Os elementos da malha cujo id eh diferente de idChave nao sao alterados -- OK
	 * 
	 * @post Uma chave CChave eh adicionada na malha -- OK
	 * @post O id desta CChave deve ser igual a idChave -- OK
	 * @post Nenhum atributo de CChave eh settado(exceto o id) -- OK
	 * @post A quantidade de elementos na malha aumenta em uma unidade -- OK
	 */
	public void adicionarChave(String idChave) {
		/* Pre */
		int totalChaves = chaves.size();
		int total = composicoes.size() + trechos.size() + chaves.size();
		
		assert(chaves.containsKey(idChave) == false);
		
		/* Inv */
		HashMap<String, CChave> oldChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> oldTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> oldComposicoes = new HashMap<String, CComposicao>(composicoes);
		oldChaves.remove(idChave);
		
		/* Codigo */
		CChave novaChave = null;
		novaChave = new CChave(idChave);
		assert(novaChave != null);
		chaves.put(idChave, novaChave);
		
		/* Post */
		assert(totalChaves + 1 == chaves.size());
		assert(total + 1 == composicoes.size() + trechos.size() + chaves.size());
		
		assert(chaves.containsKey(idChave) == true);
		
		/* Inv */
		HashMap<String, CChave> newChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> newTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> newComposicoes = new HashMap<String, CComposicao>(composicoes);
		newChaves.remove(idChave);
				
		assert(oldChaves.equals(newChaves));
		assert(oldTrechos.equals(newTrechos));
		assert(oldComposicoes.equals(newComposicoes));
	}
	
	
	/**
	 * Adiciona um trecho na malha
	 * Implementacao: OK
	 * 
	 * @param idTrecho Identificacao unica(id) de um trecho
	 * 
	 * @pre idTrecho deve ser a identificacao unica de um trecho -- OK
	 * @pre idTrecho nao pode ser igual a nenhuma outra id ja adicionada na malha -- OK
	 * 
	 * @inv Os elementos da malha cujo id eh diferente de idTrecho nao sao alterados -- OK
	 * 
	 * @post Um trecho CTrecho eh adicionada a malha -- OK
	 * @post O id deste CCTrecho deve ser igual a idTrecho -- OK 
	 * @post Nenhum atributo de CTrecho eh settado (exceto o id) -- OK
	 * @post A quantidade de elementos na malha aumenta em uma unidade -- OK
	 */
	public void adicionarTrecho(String idTrecho) {
		/* Pre */
		int totalTrechos = trechos.size();
		int total = composicoes.size() + trechos.size() + chaves.size();
		
		assert(trechos.containsKey(idTrecho) == false);
		
		/* Inv */
		HashMap<String, CChave> oldChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> oldTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> oldComposicoes = new HashMap<String, CComposicao>(composicoes);
		oldTrechos.remove(idTrecho);
		
		/* Codigo */
		CTrecho novoTrecho = null;
		novoTrecho = new CTrecho(idTrecho);
		assert(novoTrecho != null);
		
		trechos.put(idTrecho, novoTrecho);
		
		/* Post */
		assert(totalTrechos + 1 == trechos.size());
		assert(total + 1 == composicoes.size() + trechos.size() + chaves.size());
		
		assert(trechos.containsKey(idTrecho) == true);
		
		/* Inv */
		HashMap<String, CChave> newChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> newTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> newComposicoes = new HashMap<String, CComposicao>(composicoes);
		newTrechos.remove(idTrecho);
				
		assert(oldChaves.equals(newChaves));
		assert(oldTrechos.equals(newTrechos));
		assert(oldComposicoes.equals(newComposicoes));
	}
	
	
	/**
	 * Dado um elemento, encontra a chave associada a ele.
	 * Definimos chave associada como:
	 * 	- Caso elemento seja uma chave, eh a propria chave;
	 *  - Caso elemento seja uma composicao, eh a chave para onde a composicao esta indo;
	 * 	- Caso elemento seja um trecho, eh chave associada as composicoes que estao nele.
	 * 
	 * Implementacao: OK
	 * 	
	 * 
	 * @param idElemento Identificacao unica de um Elemento da Malha (Chave, Trecho ou Composicao)
	 * 
	 * @return Retorna a identificacao unica da chave associada a idElemento
	 * 
	 * @pre idElemento deve existir na malha -- OK
	 * @pre idElemento deve ser a identificacao unica de um elemento da malha -- OK
	 * 
	 * @inv Os atributos de todos os elementos da malha continuam inalterados -- OK
	 * @inv A quantidade de elementos da malha nao muda -- OK
	 * 
	 * @post A String retornada deve ser o id de um elemento existente na malha -- OK
	 * @post A String retornada deve ser a identificacao unica de uma chave -- OK
	 */
	public String encontrarChaveAssociada(String idElemento) {
		/* Pre */
				
		CChave chave = chaves.get(idElemento);
		CTrecho trecho = trechos.get(idElemento);
		CComposicao composicao = composicoes.get(idElemento);
				
		assert((chave != null) && (trecho == null) && (composicao == null) ||
				(chave == null) && (trecho != null) && (composicao == null) ||
				(chave == null) && (trecho == null) && (composicao != null));
		
		/* Inv */
		int total = composicoes.size() + trechos.size() + chaves.size();
		
		HashMap<String, CChave> oldChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> oldTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> oldComposicoes = new HashMap<String, CComposicao>(composicoes);
		
		
		
		/* Codigo */
		String chaveAssociada = null;
		
		if (chave != null) {
			chaveAssociada = chave.getId();		
		} else if (composicao != null) {
			
			CElementoTrilho elemento = composicao.getTrechoChaveAtual();
			
			if (elemento instanceof CChave) {
				chaveAssociada = elemento.getId();
			} else if (elemento instanceof CTrecho) {
				trecho = (CTrecho)elemento;
				
				if (composicao.isMovendoAParaB()) {
					if(trecho.getElementoAtualB() != null)
						chaveAssociada = trecho.getElementoAtualB().getId();
				} else {
					if(trecho.getElementoAtualA() != null)
						chaveAssociada = trecho.getElementoAtualA().getId();
				}			
			}
			
		} else if (trecho != null) {
			
			composicao = trecho.getComposicoes().peekLast();
			
			if (composicao == null) {
				chaveAssociada = null;
			} else {
				if (composicao.isMovendoAParaB()) {
					if (trecho.getElementoAtualB() != null) {
						chaveAssociada = trecho.getElementoAtualB().getId();
					} else {
						chaveAssociada = null;
					}
				} else {
					if (trecho.getElementoAtualA() != null) {
						chaveAssociada = trecho.getElementoAtualA().getId();
					} else {
						chaveAssociada = null;
					}
				}
			}
		}
		
		/* Post */
		/* Inv */
		assert(total == composicoes.size() + trechos.size() + chaves.size());
		
		HashMap<String, CChave> newChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> newTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> newComposicoes = new HashMap<String, CComposicao>(composicoes);
						
		assert(oldChaves.equals(newChaves));
		assert(oldTrechos.equals(newTrechos));
		assert(oldComposicoes.equals(newComposicoes));
		
		assert((chaveAssociada == null) || (chaves.containsKey(chaveAssociada) == true));
		
		return chaveAssociada;
	}
	
	
	/**
	 * Adiciona uma composicao ao Mapa trensACaminho da chave idChave
	 * Implementacao: OK
	 * 
	 * @param idChave Chave que recebera a composicao em seu Mapa
	 * @param idComposicao Composicao que sera adicionada ao Mapa
	 * 
	 * @pre idChave deve existir na malha -- OK
	 * @pre idChave deve ser a identificacao unica de uma chave da malha -- OK
	 * @pre idComposicao deve existir na malha -- OK
	 * @pre idComposicao deve ser a identificacao unica de uma composicao da malha -- OK
	 * 
	 * @inv Os atributos de todos os elementos da malha, exceto idChave e idComposicao continuam inalterados -- OK
	 * @inv A quantidade de elementos da malha nao muda -- OK
	 * 
	 * @post idComposicao faz parte do Mapa trensACaminho -- OK
	 */
	public void adicionarComposicaoACaminho(String idChave, String idComposicao){
		/* Pre */
		CChave chave = chaves.get(idChave);
		assert(chave != null);
		
		CComposicao composicao = composicoes.get(idComposicao);
		assert(composicao != null);
		
		/* Inv */
		int total = composicoes.size() + trechos.size() + chaves.size();
		
		HashMap<String, CChave> oldChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> oldTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> oldComposicoes = new HashMap<String, CComposicao>(composicoes);
		oldChaves.remove(idChave);
		oldComposicoes.remove(idComposicao);
		
		/* Codigo */
		chave.adicionarComposicaoACaminho(composicao);
		
		/* Post */
		assert(chave.getTrensACaminho().containsKey(composicao) == true);
		
		/* Inv */
		assert(total == composicoes.size() + trechos.size() + chaves.size());
		
		HashMap<String, CChave> newChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> newTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> newComposicoes = new HashMap<String, CComposicao>(composicoes);
		newChaves.remove(idChave);
		newComposicoes.remove(idComposicao);
						
		assert(oldChaves.equals(newChaves));
		assert(oldTrechos.equals(newTrechos));
		assert(oldComposicoes.equals(newComposicoes));
	}
	
	
	/**
	 * Remove uma composicao do Mapa trensACaminho da chave idChave
	 * Implementacao: OK
	 * 
	 * @param idChave Chave da qual sera removida a composicao de seu Mapa
	 * @param idComposicao Composicao que sera removida do Mapa
	 * 
	 * @pre idChave deve existir na malha -- OK
	 * @pre idChave deve ser a identificacao unica de uma chave da malha -- OK
	 * @pre idComposicao deve existir na malha -- OK
	 * @pre idComposicao deve ser a identificacao unica de uma composicao da malha -- OK
	 * 
	 * @inv Os atributos de todos os elementos da malha, exceto idChave e idComposicao continuam inalterados -- OK
	 * @inv A quantidade de elementos da malha nao muda -- OK
	 * 
	 * @post idComposicao deixa de constar no Mapa trensACaminho -- OK
	 */
	public void removerComposicaoACaminho(String idChave, String idComposicao) {
		/* Pre */
		CChave chave = chaves.get(idChave);
		assert(chave != null);
		
		CComposicao composicao = composicoes.get(idComposicao);
		assert(composicao != null);
		
		/* Inv */
		int total = composicoes.size() + trechos.size() + chaves.size();
		
		HashMap<String, CChave> oldChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> oldTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> oldComposicoes = new HashMap<String, CComposicao>(composicoes);
		oldChaves.remove(idChave);
		oldComposicoes.remove(idComposicao);
		
		/* Codigo */
		chave.removerComposicaoACaminho(composicao);
		
		/* Post */
		assert(chave.getTrensACaminho().containsKey(idComposicao) == false);
		
		/* Inv */
		assert(total == composicoes.size() + trechos.size() + chaves.size());
		
		HashMap<String, CChave> newChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> newTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> newComposicoes = new HashMap<String, CComposicao>(composicoes);
		newChaves.remove(idChave);
		newComposicoes.remove(idComposicao);
						
		assert(oldChaves.equals(newChaves));
		assert(oldTrechos.equals(newTrechos));
		assert(oldComposicoes.equals(newComposicoes));
	}
	
	
	/**
	 * Encontra o proximo trem que passara pela chave idChave
	 * Implementacao: OK
	 * Teste: ???
	 * 
	 * @param idChave Identiicador unico da chave
	 * 
	 * @return String representando o id de uma composicao
	 * 
	 * @pre idChave deve existir na malha -- OK
	 * @pre idChave deve ser a indetificacao unica de uma chave da malha -- OK
	 * 
	 * @inv Os atributos de todos os elementos da malha continuam inalterados -- OK
	 * @inv A quantidade de elementos da malha nao muda -- OK
	 * 
	 * @post A String de retorno eh o id da proxima composicao que passara pela chave idChave -- OK
	 */
	public String proximaComposicaoACaminho(String idChave){
		/* Pre */
		CChave chave = chaves.get(idChave);
		assert(chave != null);
		
		/* Inv */
		int total = composicoes.size() + trechos.size() + chaves.size();
		
		HashMap<String, CChave> oldChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> oldTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> oldComposicoes = new HashMap<String, CComposicao>(composicoes);
		
		
		/* Codigo */
		CComposicao compACaminho = chave.proximaComposicaoACaminho();		
		
		/* Post */
		/* Inv */
		assert(total == composicoes.size() + trechos.size() + chaves.size());
		
		HashMap<String, CChave> newChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> newTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> newComposicoes = new HashMap<String, CComposicao>(composicoes);
		
		assert(oldChaves.equals(newChaves));
		assert(oldTrechos.equals(newTrechos));
		assert(oldComposicoes.equals(newComposicoes));
		
		if(compACaminho == null)
		{
			return null;
		}
		else
		{
			return compACaminho.getId();
		}
	}
	
	
	/**
	 * Encapsulamento do metodo TrensACaminho da CChave
	 * 
	 * @param idChave ID da chave solicitada
	 * @return HashMap dos IDs das composicoes a caminho da chave
	 * 
	 * @pre idChave deve existir na malha -- OK 
	 * @pre idChave deve ser a indetificacao unica de uma chave da malha -- OK 
	 * 
	 * @inv Os atributos de todos os elementos da malha continuam inalterados -- OK
	 * @inv A quantidade de elementos da malha nao muda -- OK
	 * 
	 * @post Retorna o mesmo HashMap, mas parseado para o ID da composicao (String) ao inves dela mesma (CComposicao) -- OK
	 */
	public HashMap<String, Integer> getTrensACaminho(String idChave) {
		/* Pre */
		CChave chave = chaves.get(idChave);
		assert(chave != null);
		
		/* Inv */
		int total = composicoes.size() + trechos.size() + chaves.size();
		
		HashMap<String, CChave> oldChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> oldTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> oldComposicoes = new HashMap<String, CComposicao>(composicoes);
		
		
		/* Codigo */
		HashMap<CComposicao, Integer> mapaAntigo = chave.getTrensACaminho();
		HashMap<String, Integer> mapaNovo = new HashMap<String, Integer>();
		
		for (CComposicao composicao : mapaAntigo.keySet()) {
			mapaNovo.put(composicao.getId(), mapaAntigo.get(composicao));
		}
		
		/* Post */
		/* Inv */
		assert(total == composicoes.size() + trechos.size() + chaves.size());
		
		HashMap<String, CChave> newChaves = new HashMap<String, CChave>(chaves);
		HashMap<String, CTrecho> newTrechos = new HashMap<String, CTrecho>(trechos);
		HashMap<String, CComposicao> newComposicoes = new HashMap<String, CComposicao>(composicoes);
		
		assert(oldChaves.equals(newChaves));
		assert(oldTrechos.equals(newTrechos));
		assert(oldComposicoes.equals(newComposicoes));
		
		return mapaNovo;
	}
	
	
	/**
	 * Conjunto de chaves da malha
	 * 
	 * @return Conjunto de Chaves da malha
	 */
	public HashMap<String, CChave> getChaves() {
		return chaves;
	}
	
	
	/**
	 * Conjunto de trechos da malha
	 * 
	 * @return Conjunto de trechos da malha
	 */
	public HashMap<String, CTrecho> getTrechos() {
		return trechos;
	}
	
	
	/**
	 * Conjunto de composicoes da malha
	 * 
	 * @return Conjunto de composicoes da malha
	 */
	public HashMap<String, CComposicao> getComposicoes() {
		return composicoes;
	}

}
